using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using GarageGames.Torque.Core;
using GarageGames.Torque.Util;
using GarageGames.Torque.Sim;
using GarageGames.Torque.T3D;
using GarageGames.Torque.SceneGraph;
using GarageGames.Torque.MathUtil;



namespace TimeWar
{
    [TorqueXmlSchemaType]
    public class MovementComponent3D : T3DInputComponent, IAnimatedObject
    {
        #region Static methods, fields, constructors
        #endregion


        #region Constructors
        #endregion


        #region Public properties, operators, constants, and enums

        public float MoveSpeed
        {
            get { return _moveSpeed; }
            set { _moveSpeed = value; }
        }


        public float TurnSpeed
        {
            get { return _turnSpeed; }
            set { _turnSpeed = value; }
        }


        public bool InvertedCamera
        {
            get { return _invertedCamera; }
            set { _invertedCamera = value; }
        }


        public InputMap InputMap
        {
            get { return _inputMap; }
        }

        #endregion


        #region Public methods

        public virtual void ProcessTick(Move move, float dt)
        {

        }


        public virtual void InterpolateTick(float k)
        {
        }


        public override void CopyTo(TorqueComponent obj)
        {
            base.CopyTo(obj);

            MovementComponent3D obj2 = (MovementComponent3D)obj;
            obj2.InvertedCamera = InvertedCamera;
            obj2.PlayerIndex = PlayerIndex;
            obj2.MoveSpeed = MoveSpeed;
            obj2.TurnSpeed = TurnSpeed;
        }


        public void UpdateAnimation(float dt)
        {
            if (_currentAnimation != null)
            {
                _currentAnimation.Thread.AdvanceTime(dt);
            }
        }


        public void SetupAnimations()
        {
            //Idle Animation
            TSAnimation animationIdle = new TSAnimation();
            animationIdle.ThreadName = "ActionThread";
            animationIdle.SequenceName = "root";
            animationIdle.Name = "Idle";
            componentAnimation.AddAnimation(animationIdle);

            //Forward Animation
            TSAnimation animationForward = new TSAnimation();
            animationForward.ThreadName = "ActionThread";
            animationForward.SequenceName = "run";
            animationForward.Name = "Forward";
            componentAnimation.AddAnimation(animationForward);

            //Fall Animation
            TSAnimation animationFall = new TSAnimation();
            animationFall.ThreadName = "ActionThread";
            animationFall.SequenceName = "fall";
            animationFall.Name = "Fall";
            componentAnimation.AddAnimation(animationFall);

            //Back Animation
            TSAnimation animationBack = new TSAnimation();
            animationBack.ThreadName = "ActionThread";
            animationBack.SequenceName = "back";
            animationBack.Name = "Back";
            componentAnimation.AddAnimation(animationBack);

            //Left Animation
            TSAnimation animationLeft = new TSAnimation();
            animationLeft.ThreadName = "ActionThread";
            animationLeft.SequenceName = "side";
            animationLeft.Name = "Left";
            animationLeft.TimeScale = -1.0F;
            componentAnimation.AddAnimation(animationLeft);

            //Right Animation
            TSAnimation animationRight = new TSAnimation();
            animationRight.ThreadName = "ActionThread";
            animationRight.SequenceName = "side";
            animationRight.Name = "Right";
            componentAnimation.AddAnimation(animationRight);

            //Land Animation
            TSAnimation animationLand = new TSAnimation();
            animationLand.ThreadName = "ActionThread";
            animationLand.SequenceName = "land";
            animationLand.Name = "Land";
            componentAnimation.AddAnimation(animationLand);

            //Dead Animation
            TSAnimation animationDead = new TSAnimation();
            animationDead.ThreadName = "ActionThread";
            animationDead.SequenceName = "land";
            animationDead.Name = "Dead";
            componentAnimation.AddAnimation(animationDead);
            _currentAnimation = animationIdle;
        }

        public void FireWeapon()
        {
            Owner.Components.FindComponent<WeaponComponent3D>().FireWeapon(300);
        }

        #endregion


        #region Private, protected, internal methods

        protected override bool _OnRegister(TorqueObject owner)
        {
            if (!base._OnRegister(owner))
                return false;

            _rigidComponent = this.Owner.Components.FindComponent<T3DRigidComponent>();

            _playerSceneComponent = this.Owner.Components.FindComponent<T3DSceneComponent>();
            _camera = TorqueObjectDatabase.Instance.FindObject<T3DCameraComponent>("CameraComponent");

            return true;
        }

        protected override void _OnUnregister()
        {
            base._OnUnregister();
            PlayerManager.Instance.GetPlayer(_playerIndex).ControlObject = null;
        }


        protected override void _RegisterInterfaces(TorqueObject owner)
        {
            base._RegisterInterfaces(owner);

            // todo: register interfaces to be accessed by other components
            // E.g.,
            // Owner.RegisterCachedInterface("float", "interface name", this, _ourInterface);
        }


        protected override void _SetupInput(InputMap inputMap, int gamepad, int keyboard)
        {
            // wasd
            inputMap.BindMove(keyboard, (int)Keys.D, MoveMapTypes.StickDigitalRight, 0);
            inputMap.BindMove(keyboard, (int)Keys.A, MoveMapTypes.StickDigitalLeft, 0);
            inputMap.BindMove(keyboard, (int)Keys.W, MoveMapTypes.StickDigitalUp, 0);
            inputMap.BindMove(keyboard, (int)Keys.S, MoveMapTypes.StickDigitalDown, 0);

            // arrows
            inputMap.BindMove(keyboard, (int)Keys.Right, MoveMapTypes.StickDigitalRight, 1);
            inputMap.BindMove(keyboard, (int)Keys.Left, MoveMapTypes.StickDigitalLeft, 1);
            inputMap.BindMove(keyboard, (int)Keys.Up, MoveMapTypes.StickDigitalUp, 1);
            inputMap.BindMove(keyboard, (int)Keys.Down, MoveMapTypes.StickDigitalDown, 1);

            inputMap.BindCommand(keyboard, (int)Keys.Space, FireWeapon, null);

            //get the player object and his animation component
            TorqueObject player =
                TorqueObjectDatabase.Instance.FindObject<TorqueObject>("PlayerObject");

            componentAnimation = player.Components.FindComponent<T3DAnimationComponent>();

            //setup the animtions
            SetupAnimations();

            ProcessList.Instance.AddAnimationCallback(Owner, this);
        }

        protected override void _UpdateInput(Move move, float dt)
        {
            if (move.Sticks.Count < 2)
                return;
            //
            // Move the player object within the scene
            //
            //process rotations
            float rotX = move.Sticks[1].X;
            rotX = (float)Math.Pow(Math.Abs(rotX), Math.E) * (rotX > 0.0f ? 1.0f : -1.0f);
            float rotY = _invertedCamera ? move.Sticks[1].Y : -move.Sticks[1].Y;
            rotY = (float)Math.Pow(Math.Abs(rotY), Math.E) * (rotY > 0.0f ? 1.0f : -1.0f);
            _playerAngle = (_playerAngle - (_turnSpeed * dt * rotX)) % (2.0f * (float)Math.PI);
            SceneGroup.Rotation = Quaternion.CreateFromYawPitchRoll(0.0f, 0.0f, _playerAngle);

            //process movement
            Matrix playerTranslationMatrix = _playerSceneComponent.Transform;
            Vector3 right = MatrixUtil.MatrixGetRow(0, ref playerTranslationMatrix);
            Vector3 forward = MatrixUtil.MatrixGetRow(1, ref playerTranslationMatrix);
            Vector3 vel = ((forward * move.Sticks[0].Y) + (right * move.Sticks[0].X)) * _moveSpeed;
            _rigidComponent.Velocity = vel;

            //update the camera position
            if (_camera != null)
            {
                Vector3 cameraPosition = Vector3.Add(SceneGroup.Position, _cameraOffset);
                _camera.SetTransform(Matrix.CreateTranslation(cameraPosition), false);
                _camera.SceneGroup.Position = cameraPosition;
            }

            //
            // Animate the player object as it moves within the scene
            //
            if (move != null)
            {
                //side stepping
                if (move.Sticks[0].X > 0)
                {
                    if (_currentAnimationPlaying != CurrentAnimationPlaying.Left)
                    {
                        _currentAnimationPlaying = CurrentAnimationPlaying.Left;
                        _currentAnimation = (TSAnimation)componentAnimation.GetAnimation("Left");
                        _currentAnimation.Play();
                    }
                    return;
                }
                else if (move.Sticks[0].X < 0)
                {
                    if (_currentAnimationPlaying != CurrentAnimationPlaying.Right)
                    {
                        _currentAnimationPlaying = CurrentAnimationPlaying.Right;
                        _currentAnimation = (TSAnimation)componentAnimation.GetAnimation("Right");
                        _currentAnimation.Play();
                    }
                    return;
                }
                //forward/backward
                if (move.Sticks[0].Y > 0)
                {
                    if (_currentAnimationPlaying != CurrentAnimationPlaying.Forward)
                    {
                        _currentAnimationPlaying = CurrentAnimationPlaying.Forward;
                        _currentAnimation = (TSAnimation)componentAnimation.GetAnimation("Forward");
                        _currentAnimation.Play();
                    }
                    return;
                }
                else if (move.Sticks[0].Y < 0)
                {
                    if (_currentAnimationPlaying != CurrentAnimationPlaying.Back)
                    {
                        _currentAnimationPlaying = CurrentAnimationPlaying.Back;
                        _currentAnimation = (TSAnimation)componentAnimation.GetAnimation("Back");
                        _currentAnimation.Play();
                    }
                    return;
                }
                if (_currentAnimationPlaying != CurrentAnimationPlaying.Idle)
                {
                    _currentAnimationPlaying = CurrentAnimationPlaying.Idle;
                    _currentAnimation = (TSAnimation)componentAnimation.GetAnimation("Idle");
                    _currentAnimation.Play();
                }
            }
        }

        #endregion


        #region Private, protected, internal fields

        //input control
        int _playerIndex = 0;

        //player motion properties
        float _turnSpeed = 2.5f;
        float _moveSpeed = 4.5f;
        float _playerAngle = 0.0f;

        //camera properties
        bool _invertedCamera = false;
        Vector3 _cameraEuler = Vector3.Zero;
        T3DCameraComponent _camera;
        Vector3 _cameraOffset = new Vector3(0.0f, -15.0f, 8.0f);

        //player components
        T3DRigidComponent _rigidComponent;
        T3DAnimationComponent componentAnimation;
        T3DSceneComponent _playerSceneComponent;

        //animation properties
        TSAnimation _currentAnimation;
        CurrentAnimationPlaying _currentAnimationPlaying;

        enum CurrentAnimationPlaying
        {
            Idle,
            Forward,
            Back,
            Fall,
            Left,
            Right,
            Land,
            Dead
        }

        #endregion
    }
}
